home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
SourceCode
/
AdobeExamples
/
NX_LineDraw
/
DrawView.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
13KB
|
491 lines
/*
* (a) (C) 1990 by Adobe Systems Incorporated. All rights reserved.
*
* (b) If this Sample Code is distributed as part of the Display PostScript
* System Software Development Kit from Adobe Systems Incorporated,
* then this copy is designated as Development Software and its use is
* subject to the terms of the License Agreement attached to such Kit.
*
* (c) If this Sample Code is distributed independently, then the following
* terms apply:
*
* (d) This file may be freely copied and redistributed as long as:
* 1) Parts (a), (d), (e) and (f) continue to be included in the file,
* 2) If the file has been modified in any way, a notice of such
* modification is conspicuously indicated.
*
* (e) PostScript, Display PostScript, and Adobe are registered trademarks of
* Adobe Systems Incorporated.
*
* (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
* CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
* AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
* ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
* OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
* WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
* WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
* DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
* OF THIRD PARTY RIGHTS.
*/
/*
* DrawView.m
*
* The purpose of the application is to show different methods for drawing paths and
* the times obtained for each method. Lines are used in the construction of the paths
* but curves and arcs could be used as well.
*
* This file contains the methods for the DrawView class, a subclass of view.
* The important methods to note are the methods to draw the lines.
*
* Version: 2.0
* Author: Ken Anderson, Ken Fromm
* History:
* 03-07-91 Added this comment.
*/
#import "DrawView.h"
#import "DrawViewWraps.h"
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/TextField.h>
#import <dpsclient/wraps.h>
@implementation DrawView
/*
* A gstate is allocated for this view and the clippling is set to NO. The gstate is used
* because the view is considered important enough to have one. The clipping is set to NO
* because it can be expensive. In addition, the lines are constructed to fall entirely within the view.
* The initialization is done here because the "initailize" method is too late for instance
* variables that are involved in the display of the view.
*/
-initFrame:(const NXRect *) frameRect
{
[super initFrame:frameRect];
[self allocateGState];
[self setClipping:NO];
srand(1);
TotalLines = 0;
PSTrace = Random = NO;
PSWDefs();
return self;
}
/* Since we allocate a gstate, why not free it. In all likelihood, the gstate is freed anyways.
* A freeGState method is available, however, so conservative programming dictates that it be used.
*/
- free
{
[self freeGState];
return [super free];
}
/*
* This method is used to obtain the ids of the text fields to display the times.
*/
-setMatrixDisplayTimes:anObject;
{
matrixDisplayTimes = anObject;
return self;
}
/*
* These two methods are used to obtain the ids of the line color and line width sliders.
*/
-setSliderColor:anObject
{
sliderColor = anObject;
LineColor = [sliderColor floatValue];
return self;
}
-setSliderWidth:anObject
{
sliderWidth = anObject;
LineWidth = [sliderWidth floatValue];
return self;
}
/* Id of total lines text field. */
-setFieldTotalLines:anObject
{
fieldTotalLines = anObject;
return self;
}
/* Messaged from the button to select the line color and width. The sender is a matrix
* of buttons (a one cell matrix). The state of the button is used to key the enabling or
* disabling of the slider controls. At the end, the value of Random is flipped to make it
* sematically correct. Otherwise, YES would be no and NO would be yes.
*/
-selectColorWidth:sender
{
Random = [[sender selectedCell] state];
[sliderColor setEnabled:Random];
[sliderWidth setEnabled:Random];
Random = !Random;
return self;
}
/* The sliders message these methods. */
-changeLineColor:sender
{
LineColor = [sender floatValue];
return self;
}
-changeLineWidth:sender
{
LineWidth = [sender floatValue];
return self;
}
/* This method changes the title of the menu cell according to the
* value of the PSTrace variable.
*/
-psTrace:sender
{
if (PSTrace == NO)
[[sender selectedCell] setTitle:"Trace On"];
else
[[sender selectedCell] setTitle:"Trace Off"];
PSTrace = !PSTrace;
return self;
}
/* Erases the times. Messaged when new lines are made or the lines are cleared. */
-eraseTimes:sender
{
int i;
for (i = 0; i < [matrixDisplayTimes cellCount]; i++)
[[matrixDisplayTimes cellAt:i :0] setStringValue:""];
return self;
}
/* This method is messaged by the row of buttons to make lines. The sender is a matrix
* so the tag of the selected cell is used to obtain the number of lines to make. If the total
* number of lines exceeds the size of the line arrays, then system beep; otherwise,
* randomly create the lines and populate the arrays. If Random is set, the line color and
* width are randomly selected with each line. This is the worst case displaying lines,
* especially with the OptimizedStroke. If Random is not set then the current values
* of LineColor and LineWidth are used.
*/
-makeLines:sender
{
int Numsetlines, j;
[self eraseTimes:self];
Numsetlines = [sender selectedTag];
if (TotalLines >= MAXARRAY)
NXBeep();
else
{
if (TotalLines + Numsetlines > MAXARRAY)
Numsetlines = MAXARRAY - TotalLines;
for (j = TotalLines; j < TotalLines + Numsetlines; j++)
{
X[j] = rand () % ((int)bounds.size.width -4) +2;
Y[j] = rand () % ((int)bounds.size.height -4) +2;
X1[j] = rand () % ((int)bounds.size.width -4) +2;
Y1[j] = rand () % ((int)bounds.size.height -4) +2;
if (Random)
{
C[j] = (rand() % 1000) * 0.001;
W[j] = (rand() % (MAXWIDTH * 10)) * 0.1;
}
else
{
C[j] = LineColor;
W[j] = LineWidth;
}
}
TotalLines += Numsetlines;
[fieldTotalLines setIntValue:TotalLines];
}
return self;
}
-clearLines:sender
{
TotalLines = 0;
[fieldTotalLines setIntValue:TotalLines];
[self eraseTimes:self];
return self;
}
/*
* Messaged by the method drawing matrix of buttons.
* Messages display which in turn will message drawSelf::.
*/
-drawViewOne:sender
{
int i, row;
row = [sender selectedRow];
drawFlags.field = 0x80 >> row;
[self display];
return self;
}
/* Messaged by "Draw All" button. Messages display which in turn will message drawSelf::. */
-drawViewAll:sender
{
drawFlags.field = DRAWALL;
[self display];
return self;
}
/*
* Below are five methods. Each uses a different approach to drawing lines.
* In some cases, the approaches are similar. Each is annotated.
*/
/* This approach uses the single operator function calls which are quite easy to use because
* no wraps are necessary. The drawback is that each call is a separate message to
* the interpreter whereas a wrap is just one message. This approach is fine for simple
* drawing but wraps should be used for anything over a few lines and repetitions.
*/
-drawSingleOps:(int) cell
{
int ElapsedTime,counter;
[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
PSWMarkTime(); NXPing ();
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), YES);
PSsetgray (BGCOLOR);
PSrectfill (0.0, 0.0, bounds.size.width, bounds.size.height);
PSsetgray (BGSTRCOLOR);
PSsetlinewidth (BGSTRWIDTH);
PSrectstroke (0.0, 0.0, bounds.size.width, bounds.size.height);
for (counter = 0; counter < TotalLines; counter++)
{
PSsetlinewidth(W[counter]);
PSsetgray (C[counter]);
PSmoveto (X[counter], Y[counter]);
PSlineto (X1[counter], Y1[counter]);
PSstroke ();
}
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSWReturnTime (&ElapsedTime);
[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
return self;
}
/* This approach creates wraps to erase the view and draw the lines. The appropriate
* arguments are passed to each wrap. Compare this method with the one that follows.
* The time difference between the two is slight. As in the singler operator approach,
* this method is fine for drawings that are displayed infrequently. For drawings that
* are repeated, binding is suggested. See the comments in DrawViewWraps.psw
* for more information on binding.
*/
-drawWraps:(int) cell
{
int ElapsedTime, counter;
float ViewRect[4];
[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
PSWMarkTime (); NXPing ();
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), YES);
ViewRect[0] = ViewRect[1] = 0.0;
ViewRect[2] = bounds.size.width;
ViewRect[3] = bounds.size.height;
PSWEraseView (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
for (counter = 0; counter < TotalLines; counter++)
{
PSWDrawLine (W[counter], C[counter], X[counter], Y[counter],
X1[counter], Y1[counter]);
}
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSWReturnTime (&ElapsedTime);
[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
return self;
}
/* The wrap calls used in this approach make use of definitions contained in PSWDefs().
* The definitions bind their operators which simply means that the name lookup occurs
* at definition time only once rather than with each interpretation of the definition (which
* can occur many times). This approach is recommended for more than the simplest
* drawing. Drawings with lines that are of uniform line color and width might benefit
* from the opitimized stroke method. Random line colors and widths show this to be
* the fastest.
*/
-drawWrapsBind:(int) cell
{
int ElapsedTime, counter;
float ViewRect[4];
[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
PSWMarkTime (); NXPing ();
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), YES);
ViewRect[0] = ViewRect[1] = 0.0;
ViewRect[2] = bounds.size.width;
ViewRect[3] = bounds.size.height;
PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
for (counter = 0; counter < TotalLines; counter++)
{
PSWDrawLineBind (W[counter], C[counter], X[counter], Y[counter],
X1[counter], Y1[counter]);
}
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSWReturnTime (&ElapsedTime);
[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
return self;
}
/* This approach is uses the interpreter to loop through the array and draw the
* lines. As a result, a good deal of stack manipulation must be performed in the wrap.
* The timing results show this to be a poor approach. Not recommended at this time.
*/
-drawWrapsRepeat:(int) cell
{
int ElapsedTime;
float ViewRect[4];
[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
PSWMarkTime (); NXPing ();
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), YES);
ViewRect[0] = ViewRect[1] = 0.0;
ViewRect[2] = bounds.size.width;
ViewRect[3] = bounds.size.height;
PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
PSWDrawLineRepeatBind (W, C, X, Y, X1, Y1, TotalLines);
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSWReturnTime (&ElapsedTime);
[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
return self;
}
/* This approach builds the path with successive moveto's and lineto's until the end of the
* array or a change in line color or width occurs. At this point, the path is stroked. The
* timing studies show this to be an efficient approach when the line color and width appear
* in non-random order. As a result, this approach might be used to display a grid or other
* similar uniform drawing. NOTE: as of this writing the path stack contains a 1500 point limit.
* Therefore, a check should be included to stroke if the limit has been reached. Otherwise
* a limiterror may occur.
*/
-drawOptimizedStroke:(int) cell
{
int ElapsedTime,counter;
float ViewRect[4];
[[matrixDisplayTimes cellAt:cell :0] setStringValue:""];
PSWMarkTime (); NXPing ();
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), YES);
ViewRect[0] = ViewRect[1] = 0.0;
ViewRect[2] = bounds.size.width;
ViewRect[3] = bounds.size.height;
PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
for (counter = 0; counter < TotalLines; counter++)
{
PSWMakeLineBind (X[counter], Y[counter], X1[counter], Y1[counter]);
if (counter >= TotalLines -1 ||
C[counter] != C[counter + 1] ||
W[counter] != W[counter+1])
{
PSWStrokeLineBind (W[counter], C[counter]);
}
}
if (PSTrace)
DPSTraceContext(DPSGetCurrentContext(), NO);
PSWReturnTime (&ElapsedTime);
[[matrixDisplayTimes cellAt:cell:0] setIntValue:ElapsedTime];
return self;
}
/* Messaged by the "display" method. This method should not be called directly. */
-drawSelf:(NXRect *)r :(int) count
{
float ViewRect[4];
ViewRect[0] = ViewRect[1] = 0.0;
ViewRect[2] = bounds.size.width;
ViewRect[3] = bounds.size.height;
PSWEraseViewBind (BGCOLOR, BGSTRCOLOR, BGSTRWIDTH, ViewRect);
if (drawFlags.flags.singleops)
[self drawSingleOps:0];
if (drawFlags.flags.wraps)
[self drawWraps:1];
if (drawFlags.flags.bind)
[self drawWrapsBind:2];
if (drawFlags.flags.repeat)
[self drawWrapsRepeat:3];
if (drawFlags.flags.optimized)
[self drawOptimizedStroke:4];
return self;
}
@end